package educrypt.gui.components;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

import educrypt.gui.view.TextFilterView;
import educrypt.i18n.Messages;

class TextFilterDialog extends EducryptDialog implements TextFilterView {

	private static final long serialVersionUID = 3287931457435202618L;

	private static final int COLUMN_SYMBOLS = 0;
	private static final int COLUMN_EQUIVALENCES = 1;

	private char editingSymbol;

	private final JPanel contentPanel = new JPanel();
	private JTable table;

	private JButton btnDoFilter;

	public TextFilterDialog(Window owner) {
		super(owner);
		setTitle(Messages.getString("guiCharacterFilter"));

		this.setSize(370, 600);
		this.setPreferredSize(this.getSize());

		getContentPane().setLayout(new BorderLayout());
		getContentPane().add(contentPanel, BorderLayout.CENTER);
		contentPanel.setLayout(null);

		JLabel lblFilter = new JLabel(Messages.getString("guiCharacterFilter"));
		lblFilter.setFont(new Font("Tahoma", Font.BOLD, 15));
		lblFilter.setBounds(20, 11, 414, 24);
		contentPanel.add(lblFilter);

		initializeTable();

		{
			JPanel buttonPane = new JPanel();
			buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
			getContentPane().add(buttonPane, BorderLayout.SOUTH);
			{
				btnDoFilter = new JButton("OK");
				btnDoFilter.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						close();
					}
				});
				btnDoFilter.setActionCommand("OK");
				buttonPane.add(btnDoFilter);
				getRootPane().setDefaultButton(btnDoFilter);
			}
		}
	}

	private void initializeTable() {
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(20, 46, 320, 480);
		contentPanel.add(scrollPane);

		table = new JTable(new FilterTableModel());
		scrollPane.setViewportView(table);

		// Doesn't allow any selection
		table.setColumnSelectionAllowed(false);
		table.setRowSelectionAllowed(false);
		table.setCellSelectionEnabled(false);

		// Cancel cell edition when the dialog is being hidden
		// (only makes effect when the filtering is cancelled)
		WindowAdapter wa = new WindowAdapter() {
			@Override
			public void windowDeactivated(WindowEvent e) {
				if (table.isEditing()) {
					table.getCellEditor().cancelCellEditing();
				}
				super.windowDeactivated(e);
			}
		};
		this.addWindowListener(wa);

		// Limit the edition to one character
		JTextField oneCharacterJTextField = new JTextField();
		oneCharacterJTextField.setDocument(new LimitPlainDocument(1));

		// Show the text centered
		oneCharacterJTextField.setHorizontalAlignment(JTextField.CENTER);
		DefaultTableCellRenderer dtcr = new FilterTableDefaultTableCellRenderer();
		dtcr.setHorizontalAlignment(DefaultTableCellRenderer.CENTER);
		table.setDefaultRenderer(Object.class, dtcr);

		// Makes start cell editing with a single click
		DefaultCellEditor dtce = new DefaultCellEditor(oneCharacterJTextField);
		dtce.setClickCountToStart(1);
		table.setDefaultEditor(Object.class, dtce);

		// JTextField onFocusListener
		oneCharacterJTextField.addFocusListener(new FocusListener() {
			@Override
			public void focusLost(FocusEvent ev) {
				JTextField jtf = (JTextField) ev.getComponent();
				if (jtf.getText().equals("")) {
					jtf.setText(String.valueOf(editingSymbol));
				}
			}

			@Override
			public void focusGained(FocusEvent ev) {
				JTextField jtf = (JTextField) ev.getComponent();
				String currentSymbol = jtf.getText();
				if (currentSymbol.length() == 1) {
					editingSymbol = currentSymbol.charAt(0);
				}
				jtf.setText("");
			}
		});
	}

	@Override
	public void addListener(ActionListener ctr) {
		btnDoFilter.setActionCommand(DO_FILTER);
		btnDoFilter.addActionListener(ctr);
	}

	@Override
	public void showTableValues(List<Character> unknownSymbols,
			List<Character> equivalences,
			List<Integer> emphasizedSymbolPositions) {
		assert unknownSymbols.size() == equivalences.size();

		removeTableValues();

		FilterTableModel model = (FilterTableModel) table.getModel();

		for (int i = 0; i < unknownSymbols.size(); i++) {
			char symbol = unknownSymbols.get(i);
			char equivalence = equivalences.get(i);
			if (emphasizedSymbolPositions.contains(i)) {
				model.setCellTextBold(i, COLUMN_SYMBOLS);

			}
			table.setValueAt(symbol, i, COLUMN_SYMBOLS);
			table.setValueAt(equivalence, i, COLUMN_EQUIVALENCES);
		}
	}

	private void removeTableValues() {
		FilterTableModel model = (FilterTableModel) table.getModel();
		model.removeRows();
	}

	@Override
	public List<Character> getSymbols() {

		List<Character> symbols = new ArrayList<Character>();
		int rowSize = table.getModel().getRowCount();
		for (int i = 0; i < rowSize; i++) {
			symbols.add((Character) this.table.getValueAt(i, COLUMN_SYMBOLS));
		}
		return symbols;
	}

	@Override
	public List<Character> getEquivalences() {
		// First we must assure that any cell table is being edited
		if (table.isEditing()) {
			table.getCellEditor().stopCellEditing();
		}

		List<Character> equivalences = new ArrayList<Character>();
		int rowSize = table.getModel().getRowCount();
		for (int i = 0; i < rowSize; i++) {
			String equivalentSymbol = String.valueOf(this.table.getValueAt(i,
					COLUMN_EQUIVALENCES));
			if (equivalentSymbol.equals("")) {
				equivalentSymbol = " ";
			}
			equivalences.add(equivalentSymbol.charAt(0));
		}
		return equivalences;
	}

	class FilterTableModel extends AbstractTableModel {

		private static final long serialVersionUID = -7817793250686884154L;

		private String[] columnNames = {
				Messages.getString("guiUnknownSymbol"),
				Messages.getString("guiSubstitutionSymbol") };

		private List<List<Object>> data = new ArrayList<List<Object>>();

		private List<Integer> boldTextPos = new ArrayList<Integer>();

		@Override
		public int getColumnCount() {
			return columnNames.length;
		}

		@Override
		public int getRowCount() {
			return data.size();
		}

		@Override
		public String getColumnName(int col) {
			return columnNames[col];
		}

		@Override
		public Object getValueAt(int row, int col) {
			return data.get(row).get(col);
		}

		/*
		 * Don't need to implement this method unless your table's editable.
		 */
		@Override
		public boolean isCellEditable(int row, int col) {
			if (col < 1) {
				return false;
			} else {
				return true;
			}
		}

		/*
		 * Don't need to implement this method unless your table's data can
		 * change.
		 */
		@Override
		public void setValueAt(Object value, int row, int col) {
			while (data.size() < row + 1) {
				data.add(new ArrayList<Object>());
			}
			List<Object> columns = data.get(row);
			while (columns.size() < col + 1) {
				columns.add(new Object());
			}
			columns.set(col, value);

			fireTableCellUpdated(row, col);
		}

		public void removeRows() {
			int rowCount = data.size();
			if (rowCount > 0) {
				boldTextPos.clear();
				int lastRow = rowCount - 1;
				data.clear();
				fireTableRowsDeleted(0, lastRow);
			}
		}

		public void setCellTextBold(int row, int col) {
			boldTextPos.add(row);
		}

		public boolean isCellTextBold(int row, int col) {
			return col == COLUMN_SYMBOLS && boldTextPos.contains(row);
		}
	}

	// A Document for a JTextField with a limit in the length
	class LimitPlainDocument extends PlainDocument {

		private static final long serialVersionUID = -5243568069763672465L;

		private int limit;

		LimitPlainDocument(int limit) {
			super();
			this.limit = limit;
		}

		public void insertString(int offset, String str, AttributeSet attr)
				throws BadLocationException {
			if (str == null)
				return;

			if ((getLength() + str.length()) <= limit) {
				super.insertString(offset, str, attr);
			}
		}
	}

	// A TableCellREnderer that allows to put bold style to some cells
	class FilterTableDefaultTableCellRenderer extends DefaultTableCellRenderer {

		private static final long serialVersionUID = -7011919151633346815L;

		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int col) {
			/*
			 * Implementation Note : It is important that no "new" be present in
			 * this implementation (excluding exceptions): if the table is
			 * large, then a large number of objects would be created during
			 * rendering.
			 */
			Component component = super.getTableCellRendererComponent(table,
					value, isSelected, hasFocus, row, col);

			FilterTableModel tableModel = (FilterTableModel) table.getModel();

			Font font = component.getFont();
			if (tableModel.isCellTextBold(row, col)) {
				font = font.deriveFont(Font.BOLD);
			} else {
				font = font.deriveFont(Font.PLAIN);
			}
			component.setFont(font);

			return this;
		}
	}
}
